package com.yhy.wxzn.wcs.device.rgv.core.avoid;

import cn.hutool.core.collection.CollUtil;
import com.yhy.wxzn.common.utils.SpringUtil;
import com.yhy.wxzn.common.utils.WcsUtils;
import com.yhy.wxzn.wcs.SystemInit;
import com.yhy.wxzn.wcs.device.rgv.bean.FourWayRgv;
import com.yhy.wxzn.wcs.device.rgv.bean.Node;
import com.yhy.wxzn.wcs.device.rgv.enums.PalletStatus;
import com.yhy.wxzn.wcs.device.rgv.enums.PriorityCodes;
import com.yhy.wxzn.wcs.device.rgv.enums.RgvProgress;
import com.yhy.wxzn.wcs.entity.WcsDeviceInfo;
import com.yhy.wxzn.wcs.entity.WcsFourWayTask;
import com.yhy.wxzn.wcs.entity.WcsTask;
import com.yhy.wxzn.wcs.enums.TaskType;
import com.yhy.wxzn.wcs.service.impl.WcsFourWayTaskServiceImpl;
import com.yhy.wxzn.wcs.service.impl.WcsMapInfoServiceImpl;
import com.yhy.wxzn.wcs.service.impl.WcsTaskServiceImpl;
import com.yhy.wxzn.wcs.utils.TaskUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

@Slf4j
public class AvoidCentralManagerThread extends Thread implements AvoidCentralManager {

    private WcsMapInfoServiceImpl mapInfoService = SpringUtil.getBean(WcsMapInfoServiceImpl.class);
    private WcsTaskServiceImpl taskService = SpringUtil.getBean(WcsTaskServiceImpl.class);


    public static void main(String[] args) {
        test1();
    }

    private static void test1() {
        AvoidCentralManagerThread managerThread = new AvoidCentralManagerThread();
        ArrayList<FourWayRgv> rgvs = new ArrayList<>();

        FourWayRgv rgv1 = new FourWayRgv();
        FourWayRgv rgv2 = new FourWayRgv();
        FourWayRgv rgv3 = new FourWayRgv();
        FourWayRgv rgv4 = new FourWayRgv();


        rgv1.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv1"));
        rgv2.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv2"));
        rgv3.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv3"));
        rgv4.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv4"));


        rgv1.getWaitingForReleaseResource().addAll(Arrays.asList(rgv2, rgv3, rgv4));
        rgv2.getWaitingForReleaseResource().addAll(Arrays.asList(rgv3, rgv4));
        rgv3.getWaitingForReleaseResource().addAll(Arrays.asList(rgv2, rgv1));
        rgv4.getWaitingForReleaseResource().addAll(Arrays.asList(rgv3, rgv2, rgv1));


        rgvs.addAll(Arrays.asList(rgv1, rgv2, rgv3, rgv4));


        List<FourWayRgv> fourWayRgvs = managerThread.checkDeadLock(rgvs);

        System.out.println("=================================================================================================================");
        System.out.println("死锁车辆");
        fourWayRgvs.forEach(System.out::println);



        managerThread.avoid(fourWayRgvs);
    }
    private static void test2() {
        AvoidCentralManagerThread managerThread = new AvoidCentralManagerThread();
        ArrayList<FourWayRgv> rgvs = new ArrayList<>();

        FourWayRgv rgv1 = new FourWayRgv();
        FourWayRgv rgv2 = new FourWayRgv();
        FourWayRgv rgv3 = new FourWayRgv();
        FourWayRgv rgv4 = new FourWayRgv();
        FourWayRgv rgv5 = new FourWayRgv();
        FourWayRgv rgv6 = new FourWayRgv();
        FourWayRgv rgv7 = new FourWayRgv();
        FourWayRgv rgv8 = new FourWayRgv();


        rgv1.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv1"));
        rgv2.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv2"));
        rgv3.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv3"));
        rgv4.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv4"));
        rgv5.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv5"));
        rgv6.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv6"));
        rgv7.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv7"));
        rgv8.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv8"));


        rgv1.getWaitingForReleaseResource().addAll(Arrays.asList(rgv2));
        rgv2.getWaitingForReleaseResource().addAll(Arrays.asList(rgv3));
        rgv3.getWaitingForReleaseResource().addAll(Arrays.asList(rgv4));
        rgv4.getWaitingForReleaseResource().addAll(Arrays.asList(rgv1));
        rgv5.getWaitingForReleaseResource().addAll(Arrays.asList(rgv3));
        rgv6.getWaitingForReleaseResource().addAll(Arrays.asList(rgv4));
        rgv7.getWaitingForReleaseResource().addAll(Arrays.asList(rgv8));


        rgvs.addAll(Arrays.asList(rgv1, rgv2, rgv3, rgv4,rgv5,rgv6,rgv7,rgv8));


        List<FourWayRgv> fourWayRgvs = managerThread.checkDeadLock(rgvs);

        System.out.println("=================================================================================================================");
        System.out.println("死锁车辆");
        fourWayRgvs.forEach(System.out::println);
    }

    private static void test3() {
        AvoidCentralManagerThread managerThread = new AvoidCentralManagerThread();
        ArrayList<FourWayRgv> rgvs = new ArrayList<>();

        FourWayRgv rgv1 = new FourWayRgv();
        FourWayRgv rgv2 = new FourWayRgv();
        FourWayRgv rgv3 = new FourWayRgv();
        FourWayRgv rgv4 = new FourWayRgv();
        FourWayRgv rgv5 = new FourWayRgv();



        rgv1.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv1"));
        rgv2.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv2"));
        rgv3.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv3"));
        rgv4.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv4"));
        rgv5.setDeviceInfo(new WcsDeviceInfo().setDeviceName("rgv5"));



        rgv1.getWaitingForReleaseResource().addAll(Arrays.asList(rgv2));
        rgv2.getWaitingForReleaseResource().addAll(Arrays.asList(rgv3));
        rgv4.getWaitingForReleaseResource().addAll(Arrays.asList(rgv2));
        rgv5.getWaitingForReleaseResource().addAll(Arrays.asList(rgv2));



        rgvs.addAll(Arrays.asList(rgv1, rgv2, rgv3, rgv4,rgv5));


        List<FourWayRgv> fourWayRgvs = managerThread.checkDeadLock(rgvs);

        System.out.println("=================================================================================================================");
        System.out.println("死锁车辆");
        fourWayRgvs.forEach(System.out::println);
    }





    private void process() {
        //todo 死锁改成死锁组，每次对同组成员进行避让
        List<FourWayRgv> deadLockRgvs = checkDeadLock(SystemInit.fourWayRgvs);

        avoid(deadLockRgvs);
    }

    private void avoid(List<FourWayRgv> deadLockRgvs) {
        if (CollUtil.isEmpty(deadLockRgvs)) return;

        //todo 增加排序规则，getWaitingForReleaseResource数量相同的，按照托盘下降来进行排序
        Collections.sort(deadLockRgvs,Comparator.comparingInt(r -> r.getWaitingForReleaseResource().size()));

        for (FourWayRgv rgv : deadLockRgvs) {
            if (avoid((rgv))) break;
        }
    }

    private boolean avoid(FourWayRgv rgv) {

        //todo 对于WaitingForReleaseResource需要进行判断是否都能够获取到唯一的避让点

        int z = rgv.getCurLocation().getZ();

        HashMap<FourWayRgv, Node> hashMap = new HashMap<>();
        List<Node> map = mapInfoService.getNodeByFloor(z);
        Set<FourWayRgv> waitingForReleaseResource = rgv.getWaitingForReleaseResource();
        for (FourWayRgv avoidRgv : waitingForReleaseResource) {
            Node avoidNode = new AvoidHandler(map).getNearestAvoidNode(avoidRgv);
            if (avoidNode == null) return false;
            hashMap.put(avoidRgv,avoidNode);
        }

        for (Map.Entry<FourWayRgv, Node> entry : hashMap.entrySet()) {
            FourWayRgv needAvoidRgv = entry.getKey();
            Node avoidNode = entry.getValue();

            TaskType taskType = AvoidCentralManagerThread.ensureTaskType(needAvoidRgv);
            WcsTask task = TaskUtils.generateRgvTask(taskType, null, avoidNode, PriorityCodes.AVOID, needAvoidRgv);
            taskService.save(task);
        }

        rgv.getWaitingForReleaseResource().clear();

        for (FourWayRgv fourWayRgv : waitingForReleaseResource) {
            fourWayRgv.setAvoidIntercepted(true);
        }
        return true;
    }

    public static TaskType ensureTaskType(FourWayRgv needAvoidRgv) {
        PalletStatus palletStatus = needAvoidRgv.getFourCarPlc().getPalletStatus();

        if (palletStatus == PalletStatus.UNKNOWN) throw new RuntimeException(needAvoidRgv.getName()+"托盘状态为未知无法执行避让任务");

        if (palletStatus == PalletStatus.DOWN) return TaskType.MOVE_UNLOADED;

        if (palletStatus == PalletStatus.UP) return TaskType.MOVE_LOADED;


        //主通道
        WcsTask task = needAvoidRgv.getCurTask();
        if (task.getTaskType() == TaskType.MOVE_LOADED
                || task.getTaskType() == TaskType.MOVE_UNLOADED
                || task.getTaskType() == TaskType.CHARGE)
            return task.getTaskType();


        //移、出、入库
        if (task.getRgvProgress() == RgvProgress.RGV_TO_END)
            return TaskType.MOVE_LOADED;
        else
            return TaskType.MOVE_UNLOADED;
    }



    /**
     * 获取所有发生死锁的车辆
     *
     * @param rgvs
     * @return
     */
    private List<FourWayRgv> checkDeadLock(List<FourWayRgv> rgvs) {
        HashSet<FourWayRgv> res = new HashSet<>();
        HashSet<FourWayRgv> visited = new HashSet<>();
        Stack<FourWayRgv> recordPath = new Stack<>();

        rgvs.forEach(rgv ->  dfs(visited, recordPath, res, rgv));

        return new ArrayList<>(res);
    }


    private void dfs(HashSet<FourWayRgv> visited, Stack<FourWayRgv> recordPath, HashSet<FourWayRgv> res, FourWayRgv curRgv) {
        visited.add(curRgv);
        recordPath.push(curRgv);

        for (FourWayRgv resourceRgv : curRgv.getWaitingForReleaseResource()) {
            if (recordPath.contains(resourceRgv)) {
                res.addAll(recordPath);
            }else if (res.contains(resourceRgv)){
                res.add(curRgv);
            }else if (!visited.contains(resourceRgv)) {
                dfs(visited, recordPath, res, resourceRgv);
            }
        }
        recordPath.pop();
    }


    @Override
    public void run() {
        while (true) {
            try {
                WcsUtils.sleep(1000);
                process();
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }


}
